-
Notifications
You must be signed in to change notification settings - Fork 242
Add has_closure_tree_roots (plural) to allow has_many in related AR models #446
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add has_closure_tree_roots (plural) to allow has_many in related AR models #446
Conversation
This seems like a lot of change. Have you tried either of these ways?: STI or ignoring the post in the comment model. STI would have Comment as a child class of Post. Not sure that I like this, but some people like STI. Treat the Comment as a tree form, but just have a post.comments.roots
Comment.where(:post_id => post.id).roots Besides |
Well, the challenging thing for this change was tests. I would say 90% of this PR is setting up the test project + specs. Otherwise we're using almost the same code with a bit of changes. But because I don't want to break any backwards compatibility there was a bit of copy paste. About STI, I would really like to avoid it at all cost. It has cause me a lot of pain the past. About your other solution, I'm not sure if it will provide the same benefits of n+1 query avoidance. If I have 100 roots (eg. 100 root comments on a post), it will result in 100 queries for the children of each of the root nodes? Perhaps I'm missing something from the eager loading methods, like If anyone would like to help me add documentation on how to handle eager loading with multi root has_many, I'm happy to participate in whatever way I can. |
To summarize -- There are 300+ lines but the core of the change are just these lines # For multiple roots, fetch all descendants at once to avoid N+1 queries
root_ids = roots.map(&:id)
klass = roots.first.class
hierarchy_table = klass._ct.quoted_hierarchy_table_name
# Get all descendants of all roots including the roots themselves
# (generations 0 = self, > 0 = descendants)
descendant_scope = klass.
joins("INNER JOIN #{hierarchy_table} ON #{hierarchy_table}.descendant_id = #{klass.quoted_table_name}.#{klass.primary_key}").
where("#{hierarchy_table}.ancestor_id IN (?)", root_ids).
includes(*assoc_map).
distinct
descendant_scope |
lol - yes. My teammates no longer listen when I say "lets get rid of the STI in model X." Different people are in different places on that debate. Seems many of the users of
I think we agree that the perfect case requires 2 queries: Wish it were easier to say to active record: "I've fetched these records because you didn't know how to fetch them for I was thinking:
|
Having
has_closure_tree_root
is quite useful, when there is a root instance that collects all children.However I have the use case where a related model has multiple roots. This is not a case of multi-parent hierarchy, but instead a related object having multiple roots. The example in the tests is one of them.
Post
has many comments, and those comments can have children. Withhas_closure_tree_root
we would be forced to create a root comment to each post, from which all members would be decendents from to use the query helpers.This allows a related model to have multiple associated roots, and have the same perfomance boost in the queries as with the
has_closure_tree_root.
.NOTE I was assisted by an AI agent while doing this work, and some of the code here is generated by a model. It has been reviewed and iterated by me.